package pacman.entries.pacman;

import java.util.ArrayList;
import java.util.Collection;

import pacman.controllers.Controller;
import pacman.game.Constants.MOVE;
import pacman.game.Game;
import edu.ucsc.gameAI.Debug;
import edu.ucsc.gameAI.EatPills;
import edu.ucsc.gameAI.IAction;
import edu.ucsc.gameAI.MoveTowardsClosestEdibleGhostAction;
import edu.ucsc.gameAI.MoveTowardsClosestPowerPillAction;
import edu.ucsc.gameAI.RunAwayFromGhosts;
import edu.ucsc.gameAI.conditions.AreAnyGhostsEdible;
import edu.ucsc.gameAI.conditions.DangerousGhostNearby;
import edu.ucsc.gameAI.conditions.DangerousGhostNearbyANDPowerPillsRemain;
import edu.ucsc.gameAI.conditions.NoDangerousGhostsNearby;
import edu.ucsc.gameAI.conditions.NoEdibleGhostsRemaining;
import edu.ucsc.gameAI.fsm.ITransition;
import edu.ucsc.gameAI.fsm.State;
import edu.ucsc.gameAI.fsm.StateMachine;
import edu.ucsc.gameAI.fsm.Transition;

/*
 * This is the class you need to modify for your entry. In particular, you need to
 * fill in the getAction() method. Any additional classes you write should either
 * be placed in this package or sub-packages (e.g., game.entries.pacman.mypackage).
 */
public class MyPacMan extends Controller<MOVE> {

	// Decision Tree implementation
	// private MOVE myMove = MOVE.NEUTRAL;
	// private BinaryDecision isGhostNearby, isPPNearby, isPelletNearby,
	// isSafePathToNearestPP, isSafePathToNearestPellet;
	// private IBinaryNode runAwayFromGhosts, moveTowardCloesestPellet,
	// moveTowardPP, moveTowardNotClosestPellet;

	// FSM Implementation
	private State eatPillsState;
	private State headTowardPPState;
	private State PPEatean;
	private State eatGhostsState;
	private State avoidDanger;
	private StateMachine fsm;

	public MyPacMan() {

		// BST
		// isGhostNearby = new BinaryDecision();
		// moveTowardCloesestPellet = new EatPills();
		// runAwayFromGhosts = new RunAwayFromGhosts();
		//
		// isGhostNearby.setCondition(new DangerousGhostNearby());
		// isGhostNearby.setTrueBranch(runAwayFromGhosts);
		// isGhostNearby.setFalseBranch(moveTowardCloesestPellet);

		// FSM
		fsm = new StateMachine();

		eatPillsState = new State("eatPillsState");
		headTowardPPState = new State("headTowardPPState");
		eatGhostsState = new State("eatGhostsState");
		Collection<ITransition> eatPillsStateTrans = new ArrayList<ITransition>();
		Transition tran1 = new Transition("eatPills->headTowardsPP (Cond: DangerousGhostNearbyANDPowerPillsExist)");
		tran1.setCondition(new DangerousGhostNearbyANDPowerPillsRemain());
		tran1.setTargetState(headTowardPPState);
		tran1.setAction(new MoveTowardsClosestPowerPillAction());
		eatPillsStateTrans.add(tran1);
		
		//TECHNICALLY this shouldn't happen, but on the off chance that pacman accidentally eats a power pill
		//we he wasn'ts upposed to, we still want him to take advantage of his new found powers and chase after 
		//the weak ghosts.
		Transition eatPillsTran2 = new Transition("eatPills->killNearbyGhosts (Cond: EdibleGhostExist)");
		eatPillsTran2.setCondition(new AreAnyGhostsEdible());
		eatPillsTran2.setTargetState(eatGhostsState);
		eatPillsTran2.setAction(new MoveTowardsClosestEdibleGhostAction());
		eatPillsStateTrans.add(eatPillsTran2);
		
		eatPillsState.setTransitions(eatPillsStateTrans);
		eatPillsState.setAction(new EatPills());

		
		//headTowardsPPState (transition definitions)
		Collection<ITransition> headTowardPPStateTrans = new ArrayList<ITransition>();
		
//		Transition tran2 = new Transition("headTowardsPP->eatPills (Cond: AreAllPowerpillsGone)");
//		tran2.setCondition(new AreAllPowerPillsGone());
//		tran2.setTargetState(eatPillsState);
//		tran2.setAction(new EatPills());
//		headTowardPPStateTrans.add(tran2);
		
		Transition tran3 = new Transition("headTowardsPP->eatPills (Cond: NoDangerousGhostsNearby)");
		tran3.setCondition(new NoDangerousGhostsNearby());
		tran3.setTargetState(eatPillsState);
		tran3.setAction(new EatPills());
		headTowardPPStateTrans.add(tran3);
		
		Transition tran4 = new Transition("headTowardsPP->eatGhosts (Cond: EdibleGhostsExist)");
		tran4.setCondition(new AreAnyGhostsEdible());
		tran4.setTargetState(eatGhostsState);
		tran4.setAction(new MoveTowardsClosestEdibleGhostAction());
		headTowardPPStateTrans.add(tran4);
		
		headTowardPPState.setTransitions(headTowardPPStateTrans);
		headTowardPPState.setAction(new MoveTowardsClosestPowerPillAction());
		
		
		
		//Defining the eatGhostsState.
		Collection<ITransition> eatGhostsStateTransitions = new ArrayList<ITransition>();
		Transition tran5 = new Transition("EatGhostsState->EatRegularPills (Cond NoEdibleGhostsExist)");
		tran5.setCondition(new NoEdibleGhostsRemaining());
		tran5.setTargetState(eatPillsState);
		tran5.setAction(new EatPills());
		eatGhostsStateTransitions.add(tran5);
		
		eatGhostsState.setTransitions(eatGhostsStateTransitions);
		eatGhostsState.setAction(new MoveTowardsClosestEdibleGhostAction());
		
		fsm.setCurrentState(eatPillsState); 
		
	}


	public MOVE getMove(Game game, long timeDue) {

		if(game.wasPacManEaten()){
			Debug.debug("OOOF! PACMAN GOT EATEN! Live Remaining: " + game.getPacmanNumberOfLivesRemaining(), Debug.MESSAGETYPE.STATEMENT);
		}
		
		// FSM
		Collection<IAction> actions = fsm.update(game);
		//System.out.println("action list has:" + actions.size());

		

		
		for (IAction action : actions) {
			Debug.debug(action.toString(), Debug.MESSAGETYPE.STATEMENT);
			if (action.getClass() == RunAwayFromGhosts.class)
				return new RunAwayFromGhosts().getMove(game);
			else if (action.getClass() == EatPills.class)
				return new EatPills().getMove(game);
			else if (action.getClass() == MoveTowardsClosestPowerPillAction.class)
				return new MoveTowardsClosestPowerPillAction().getMove(game);
			else if (action.getClass() == MoveTowardsClosestEdibleGhostAction.class)
				return new MoveTowardsClosestEdibleGhostAction().getMove(game);
			else {
				Debug.debug("No recognized actions returned! Returning a default value for getMove", Debug.MESSAGETYPE.STATEMENT);
				return action.getMove();
			}
		}
		Debug.debug("We didn't even make it into the forloop for some reason.", Debug.MESSAGETYPE.STATEMENT);
		return null;

		// BST
		// IAction action = isGhostNearby.makeDecision(game);
		// return action.getMove();
		// return isGhostNearby.makeDecision(game).getMove();
	}
}